package org.example.myleet.p736;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Solution {
    public int evaluate(String expression) {
        if (expression.startsWith("(")) {
            return evaluate(expression.substring(1, expression.length() - 1));
        }
        return evaluateWithParameters(expression, new HashMap<>());
    }

    private int evaluateWithParameters(String expression, Map<String, Integer> parameters) {
        if (expression.startsWith("(")) {
            //有括号的移除括号
            return evaluateWithParameters(expression.substring(1, expression.length() - 1), parameters);
        }
        //将移除括号后裸露出来的表达式进行按空格切分成不同的元素，被括号包裹的表达式则作为一个元素
        String[] elements = splitExpression(expression);
        if (elements.length == 1) {
            //若只剩下一个元素，要么这个元素可以转换为数字，要么这个元素的结果是在传递进来的参数parameters里面
            char c0 = elements[0].charAt(0);
            if (Character.isDigit(c0) || '-' == c0) {
                return Integer.parseInt(elements[0]);
            }
            return parameters.getOrDefault(elements[0], 0);
        }
        //元素不止一个，则按照不同的指令进行解释
        if (elements[0].equals("let")) {
            //解析let指令，let指令主要是在当前作用域进行赋值操作
            //首先创建当前作用域的变量map，继承自传递进来的parameters
            Map<String, Integer> params = new HashMap<>(parameters);
            for (int i = 1; i < elements.length - 1; ++i) {
                String paramName = elements[i++];
                int value;
                String expr = elements[i];
                //对表达式的解析有两种方式
                if (expr.startsWith("(")) {
                    //若表达式是一个包含指令的复杂表达式，则递归处理
                    value = evaluateWithParameters(expr.substring(1, expr.length() - 1), params);
                } else {
                    //若表达式是不含指令的简单表达式，则要么这个元素可以转换为数字，要么这个元素的结果是在当前作用域params里面
                    char c0 = expr.charAt(0);
                    if (Character.isDigit(c0) || '-' == c0) {
                        value = Integer.parseInt(expr);
                    } else {
                        value = params.getOrDefault(expr, 0);
                    }
                }
                //找到一对"变量->值"的映射，加入到作用域变量集合中
                params.put(paramName, value);
            }
            //对于let指令的最后一个表达式进行解析，递归处理
            return evaluateWithParameters(elements[elements.length - 1], params);
        } else {
            //下面是计算指令，add或mul，都需要先解析出value1和value2
            int value1, value2;
            //以value1为例
            if (elements[1].startsWith("(")) {
                //带括号表面是复杂表达式，递归处理
                value1 = evaluateWithParameters(elements[1].substring(1, elements[1].length() - 1), parameters);
            } else {
                //不带括号则要么这个元素可以转换为数字，要么这个元素的结果是在当前作用域parameters里面
                char c0 = elements[1].charAt(0);
                if (Character.isDigit(c0) || '-' == c0) {
                    value1 = Integer.parseInt(elements[1]);
                } else {
                    value1 = parameters.getOrDefault(elements[1], 0);
                }
            }
            if (elements[2].startsWith("(")) {
                value2 = evaluateWithParameters(elements[2].substring(1, elements[2].length() - 1), parameters);
            } else {
                char c0 = elements[2].charAt(0);
                if (Character.isDigit(c0) || '-' == c0) {
                    value2 = Integer.parseInt(elements[2]);
                } else {
                    value2 = parameters.getOrDefault(elements[2], 0);
                }
            }
            if (elements[0].equals("add")) {
                return value1 + value2;
            } else {
                return value1 * value2;
            }
        }
    }

    /**
     * 表达式进行按空格切分成不同的元素，被括号包裹的表达式则作为一个元素
     */
    private String[] splitExpression(String expression) {
        List<String> elements = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        char c;
        for (int i = 0; i < expression.length(); ++i) {
            c = expression.charAt(i);
            if (c == ' ') {
                //遇到空格，切分出一个元素
                elements.add(sb.toString());
                sb.delete(0, sb.length());
            } else if (c == '(') {
                //遇到括号，需要将括号内的内容放到一个元素里面
                sb.append(c);
                ++i;
                int level = 1;
                while (level > 0) {
                    c = expression.charAt(i);
                    //栈的思想，要配对完整的括号
                    if (c == ')') {
                        --level;
                    } else if (c == '(') {
                        ++level;
                    }
                    sb.append(c);
                    ++i;
                }
                --i;
            } else {
                sb.append(c);
            }
        }
        elements.add(sb.toString());
        return elements.toArray(new String[0]);
    }
}
